package com.example.design.patterns.geekbang.结构型.结构类似的模式.代理模式.DynamicProxy;

import com.example.design.patterns.geekbang.结构型.结构类似的模式.代理模式.cases.MetricsCollector;
import com.example.design.patterns.geekbang.结构型.结构类似的模式.代理模式.cases.RequestInfo;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

/**
 * 代理模式: 在不改变原始类（或叫被代理类）的情况下，通过引入代理类来给原始类附加功能
 * 原始类-静态代理:
 * 1.定义了接口: 让代理类和原始类实现同样的接口
 * 2.没有定义接口: 让代理类继承原始类的方法来实现代理模式
 *
 * 动态代理:在运行的时候动态地创建原始类对应的代理类，然后在系统中用代理类替换掉原始类。
 *
 * 适用范围: 常用在业务系统中开发一些非功能性需求，比如：监控、统计、鉴权、限流、事务、幂等、日志。
 */
public class MetricsCollectorProxy {
    private MetricsCollector metricsCollector;

    public MetricsCollectorProxy() {
        this.metricsCollector = new MetricsCollector();
    }

    public Object createProxy(Object proxiedObject) {
        Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
        DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
        return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
    }

    private class DynamicProxyHandler implements InvocationHandler {
        private Object proxiedObject;

        public DynamicProxyHandler(Object proxiedObject) {
            this.proxiedObject = proxiedObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long startTimestamp = System.currentTimeMillis();
            Object result = method.invoke(proxiedObject, args);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            String apiName = proxiedObject.getClass().getName() + ":" + method.getName();
            RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimestamp);
            metricsCollector.recordRequest(requestInfo);
            return result;
        }
    }
}


